#include <windows.h>
#include <iostream>
using namespace std;

#include "service.hpp"
#include "worm.hpp"
#include "multithreading.hpp"
#include "settings.hpp"
using namespace multithreading;

SERVICE_STATUS          serviceStatus;
SERVICE_STATUS_HANDLE   serviceStatusHandle;

event                   *serviceStopEvent;
bool                    serviceRunningAsService = FALSE;

void WINAPI serviceMain(DWORD argc, LPTSTR *argv){ //Entrypoint for service
    serviceStatusHandle = RegisterServiceCtrlHandler(settings.child("service").value("name").c_str(),serviceCtrlHandler);
    if (!serviceStatusHandle){
        log(LOG_TYPE_ERROR,"Could not register service control handler");
        return;
    }

    serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
    serviceStatus.dwServiceSpecificExitCode = 0;

    reportServiceStatus(SERVICE_START_PENDING,NO_ERROR,3000);

    //serviceCtrlHandler will signal this event when the service should stop
    serviceStopEvent = new event;

    reportServiceStatus(SERVICE_RUNNING,NO_ERROR,0);

    service_pre();
    while(1){
        service_loop();
        if (serviceStopEvent->isSet()) break;                    //Check if the stop-event has been signaled
    }
    service_post();

    quit();
}

void quit(){
    if (serviceRunningAsService) reportServiceStatus(SERVICE_STOPPED,NO_ERROR,0);         // Set service status to "STOPPED"
    exit(0);                                                                              // and exit
}

void WINAPI serviceCtrlHandler(DWORD ctrl){
   switch(ctrl) {
      case SERVICE_CONTROL_STOP:
         reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

         // Signal the service to stop.
         serviceStopEvent->set();

         return;
      case SERVICE_CONTROL_INTERROGATE:
         // Fall through to send current status.
         break;
      default:
         break;
   }
   reportServiceStatus(serviceStatus.dwCurrentState, NO_ERROR, 0);
}

void service(int argc, char *argv[]){ //To launch the service
    serviceRunningAsService = TRUE;


    char *servicename = new char[settings.child("service").value("name").length()+1];
    strcpy(servicename,settings.child("service").value("name").c_str());

    SERVICE_TABLE_ENTRY DispatchTable[] = {
        { servicename, (LPSERVICE_MAIN_FUNCTION) serviceMain },
        { NULL, NULL }
    };

    if (!StartServiceCtrlDispatcher(DispatchTable)){
        log(LOG_TYPE_ERROR,"Could not launch service");
    }
}

void reportServiceStatus(DWORD currentState,DWORD exitCode,DWORD wait){
    static DWORD checkPoint = 1;

    // Fill in the SERVICE_STATUS structure.

    serviceStatus.dwCurrentState = currentState;
    serviceStatus.dwWin32ExitCode = exitCode;
    serviceStatus.dwWaitHint = wait;

    if (currentState == SERVICE_START_PENDING)
    serviceStatus.dwControlsAccepted = 0;
    else serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

    if ((currentState == SERVICE_RUNNING) || (currentState == SERVICE_STOPPED))
    serviceStatus.dwCheckPoint = 0;
    else serviceStatus.dwCheckPoint = checkPoint++;

    SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

bool log(int type,const char *message){
    if (serviceRunningAsService){
        HANDLE es = RegisterEventSource(NULL,settings.child("service").value("name").c_str());
        if (es == NULL){
            return false;
        }

        const char *strings[2];
        strings[0] = settings.child("service").value("name").c_str();
        strings[1] = message;

        ReportEvent(es,type,0,0,NULL,2,0,strings,NULL);

        DeregisterEventSource(es);

        return true;
    } else {
        switch(type){
            case LOG_TYPE_INFO: cout << "INFO:\t"; break;
            case LOG_TYPE_WARNING: cout << "WARNING:\t"; break;
            case LOG_TYPE_ERROR: cout << "ERROR:\t"; break;
        }
        cout << message << endl;
        return true;
    }
}















